/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Xceed.Silverlight.Data;
using Xceed.Silverlight.Utils;
using System.Windows.Input;

namespace Xceed.Silverlight.Controls
{
  [TemplateVisualState( GroupName = "LicensingStates", Name = "Licensed" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "NotLicensed" )]
  public class FilterControl : Control
  {
    #region Constructor

    public FilterControl()
    {

      this.DefaultStyleKey = typeof( FilterControl );

      this.Loaded += new RoutedEventHandler( this.OnLoaded );
      this.Unloaded += new RoutedEventHandler( this.OnUnloaded );

      m_dispatcherTimer = new DispatcherTimer();
      m_dispatcherTimer.Tick += this.OnApplyDataSourceFilterExpressionTimerTick;
      m_dispatcherTimer.Interval = this.ApplyFilterInterval;
    }

    private void OnUnloaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
    }

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
    }

    #endregion

    #region ApplyFilterInterval Property

    public static readonly DependencyProperty ApplyFilterIntervalProperty = DependencyProperty.Register(
      "ApplyFilterInterval",
      typeof( TimeSpan ),
      typeof( FilterControl ),
      new PropertyMetadata(
        TimeSpan.FromMilliseconds( 700 ),
        new PropertyChangedCallback( FilterControl.OnApplyFilterIntervalChanged ) ) );

    public TimeSpan ApplyFilterInterval
    {
      get
      {
        return ( TimeSpan )this.GetValue( FilterControl.ApplyFilterIntervalProperty );
      }
      set
      {
        this.SetValue( FilterControl.ApplyFilterIntervalProperty, value );
      }
    }

    private static void OnApplyFilterIntervalChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      FilterControl filterControl = sender as FilterControl;

      if( filterControl == null )
        return;

      if( filterControl.m_dispatcherTimer == null )
        return;

      filterControl.m_dispatcherTimer.Interval = ( TimeSpan )e.NewValue;
    }

    #endregion

    #region ClearFilterButtonVisibility Property

    public static readonly DependencyProperty ClearFilterButtonVisibilityProperty = DependencyProperty.Register(
      "ClearFilterButtonVisibility",
      typeof( Visibility ),
      typeof( FilterControl ),
      new PropertyMetadata( Visibility.Visible ) );

    public Visibility ClearFilterButtonVisibility
    {
      get
      {
        return ( Visibility )this.GetValue( FilterControl.ClearFilterButtonVisibilityProperty );
      }
      set
      {
        this.SetValue( FilterControl.ClearFilterButtonVisibilityProperty, value );
      }
    }

    #endregion

    #region ClearFilterCommand Property

    public ICommand ClearFilterCommand
    {
      get
      {
        if( m_clearFilterCommand == null )
        {
          m_clearFilterCommand = new Command( new Action<object>( this.ClearFilterExcecute ),
            new Func<object, bool>( this.ClearFilterCanExecute ) );
        }

        return m_clearFilterCommand;
      }
    }

    protected virtual bool ClearFilterCanExecute( object parameter )
    {
      return ( this.DataSourceFilterExpression != null );
    }

    protected virtual void ClearFilterExcecute( object parameter )
    {
      this.DataSourceFilterExpression = null;
      this.UIFilterExpression = null;
    }

    private Command m_clearFilterCommand;

    #endregion

    #region UIFilterExpression Property

    public static readonly DependencyProperty UIFilterExpressionProperty = DependencyProperty.Register(
      "UIFilterExpression",
      typeof( FilterExpressionBase ),
      typeof( FilterControl ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( FilterControl.OnUIFilterExpressionChanged ) ) );

    public FilterExpressionBase UIFilterExpression
    {
      get
      {
        return ( FilterExpressionBase )this.GetValue( FilterControl.UIFilterExpressionProperty );
      }
      set
      {
        this.SetValue( FilterControl.UIFilterExpressionProperty, value );
      }
    }

    private static void OnUIFilterExpressionChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      FilterControl filterControl = sender as FilterControl;

      if( filterControl == null )
        return;

      filterControl.OnUIFilterExpressionChanged( e.OldValue as FilterExpressionBase, e.NewValue as FilterExpressionBase );
    }

    #endregion

    #region DataSourceFilterExpression Property

    public static readonly DependencyProperty DataSourceFilterExpressionProperty = DependencyProperty.Register(
      "DataSourceFilterExpression",
      typeof( FilterExpressionBase ),
      typeof( FilterControl ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( FilterControl.OnDataSourceFilterExpressionChanged ) ) );

    public FilterExpressionBase DataSourceFilterExpression
    {
      get
      {
        return ( FilterExpressionBase )this.GetValue( FilterControl.DataSourceFilterExpressionProperty );
      }
      set
      {
        this.SetValue( FilterControl.DataSourceFilterExpressionProperty, value );
      }
    }

    private static void OnDataSourceFilterExpressionChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      FilterControl filterControl = sender as FilterControl;

      if( filterControl == null )
        return;

      filterControl.OnDataSourceFilterExpressionChanged( e.OldValue as FilterExpressionBase, e.NewValue as FilterExpressionBase );
    }

    #endregion

    #region Protected methods

    protected void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    protected void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLicensingStates( useTransitions );
      this.ChangeVisualStateCore( useTransitions );
    }

    protected internal virtual void ChangeVisualStateCore( bool useTransitions )
    {
    }

    #endregion

    #region Filtering

    protected virtual void OnUIFilterExpressionChanged( FilterExpressionBase oldValue, FilterExpressionBase newValue )
    {
      m_lastFilterExpressionBase = newValue;

      // Do not use the DispatcherTimer when the new FilterExpression is null
      // which means ClearFilterCommand was executed or ClearFilter method was called
      if( newValue == null )
      {
        this.OnApplyDataSourceFilterExpressionFromUIFilterExpressionBase( m_lastFilterExpressionBase );
      }
      else
      {
        m_dispatcherTimer.Start();
      }

      if( m_clearFilterCommand != null )
        m_clearFilterCommand.RaiseCanExecuteChanged();
    }

    protected virtual void OnDataSourceFilterExpressionChanged( FilterExpressionBase oldValue, FilterExpressionBase newValue )
    {
      // Update UIFilterExpresion immediately to let any bound control
      // to update its visual state immediately
      this.UIFilterExpression = this.DataSourceFilterExpression;

      if( m_clearFilterCommand != null )
        m_clearFilterCommand.RaiseCanExecuteChanged();
    }

    protected virtual void OnApplyDataSourceFilterExpressionFromUIFilterExpressionBase( FilterExpressionBase filterExpressionBase )
    {
      // Before applying the DataSourceFilter expression, make sure
      // we are not trying to apply the same expression.
      if( this.DataSourceFilterExpression != filterExpressionBase )
      {
        this.DataSourceFilterExpression = filterExpressionBase;
      }
    }

    private void OnApplyDataSourceFilterExpressionTimerTick( object sender, EventArgs e )
    {
      this.OnApplyDataSourceFilterExpressionFromUIFilterExpressionBase( m_lastFilterExpressionBase );
      m_dispatcherTimer.Stop();
      m_lastFilterExpressionBase = null;
    }

    #endregion

    #region Private Methods

    private void UpdateLicensingStates( bool useTransitions )
    {
      if( m_isLicensed )
      {
        VisualStateManager.GoToState( this, "Licensed", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "NotLicensed", useTransitions );
      }
    }

    #endregion

    #region Private fields

    private FilterExpressionBase m_lastFilterExpressionBase;
    private DispatcherTimer m_dispatcherTimer;

    //licensing
    private readonly Guid m_designTimeGuid;
    private readonly bool m_isInDesigner;
    private bool m_isLicensed = true;

    #endregion
  }
}
